import get from 'lodash-es/get.js';
import set from 'lodash-es/set.js';

function getOption(options, defaultOption) {
  const opt = { defaultValue: null, dataType: null, validate: null };
  options = Object.assign({}, opt, defaultOption, options);
  if (typeof options.dataType !== 'string') {
    options.dataType = null;
  }
  return options;
}

function getDefaultValue(type) {
  const defaultValue = {
    Function: () => {},
    Object: {},
    Array: [],
    Number: 0,
    String: '',
    Null: null,
  };
  return defaultValue[type];
}

export function isVoid(v) {
  return typeof v === 'undefined' || v === null;
}

export function getDataType(data) {
  return Object.prototype.toString.call(data).slice(8, -1);
}

export function getValue(data, key, options) {
  options = getOption(options);
  let { dataType, validate, defaultValue } = options;
  if (typeof validate !== 'function') {
    validate = (v) => {
      return dataType ? dataType === getDataType(v) : true;
    };
  }
  if (typeof key === 'string' && key) {
    key = key.replace(/\s/g, '');
    const keys = key.split(/\|{1,2}/).filter(Boolean);
    for (const field of keys) {
      let val = get(data, field);
      if (validate(val)) return val;
    }
  }
  return dataType ? getDefaultValue(dataType) : defaultValue;
}

export const getValidValue = (sources = [], field, options) => {
  if (!sources || !sources.length || !field) return;
  options = getOption(options);
  if (typeof options.validate !== 'function') {
    options.validate = (v) => {
      return !isVoid(v) && v !== '' && (typeof v === 'number' ? Number.isFinite(v) : true);
    };
  }
  for (let i = 0, len = sources.length; i < len; i++) {
    const val = getValue(sources[i], field, options);
    if (val !== options.defaultValue) return val;
  }
};

export function getReplacedData(dataSource, fieldsMap, options) {
  options = getOption(options);
  if (typeof dataSource !== 'object' || !dataSource) {
    dataSource = {};
  }
  if (typeof fieldsMap !== 'object' || !fieldsMap) {
    fieldsMap = {};
  }
  if (Array.isArray(fieldsMap)) {
    fieldsMap = fieldsMap.reduce((res, item) => {
      return Object.assign(res, { [item]: item });
    }, {});
  }
  return Object.keys(fieldsMap).reduce((res, key) => {
    return Object.assign(res, {
      [key]: getValue(dataSource, fieldsMap[key], options),
    });
  }, {});
}

export function mergeValidData(target, sources, options) {
  if (typeof target !== 'object' || !target || !sources || !sources.length) {
    return;
  }
  const getType = (data) => {
    return Object.prototype.toString.call(data).slice(8, -1);
  };
  const typeEnum = { Object: {}, Array: [] };
  options = getOption(options);
  return Object.keys(target).reduce((res, key) => {
    const val = getValidValue(sources, key, options);
    return Object.assign(res, {
      [key]: val !== options.defaultValue ? val : target[key],
    });
  }, typeEnum[getType(target)] || typeEnum.Object);
}
